﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace Maestro_Proveedores.clases.almacen
{
    class SuavExpDoble : ModeloAbstracto
    {

        
        private double alfaOptimo;
        private double a1, b1, b2, S0, S02;
        ArrayList resultadosN;
      
        //-------------------------CONSTRUCTOR DE CLASE

         public SuavExpDoble()
        {
            this.nombreModelo = "Suavizacion Exponencial Doble";
            this.resultadosN = new System.Collections.ArrayList();
            resNideal = new Resultado();
        }

        //------------------------ INICIO METODOS SOBREESCRITOS

        public override Resultado obtenerResultado()
        {
            return this.resNideal;
        }

        public override void pronosticoSemanaN()
        {
            // se realiza una copia de la lista original, para cada calculo de N
            System.Collections.ArrayList registrosClon = new System.Collections.ArrayList();
            registrosClon = copiarArrayListSED(registros);
            semanaPronostico = registros.Count;
            RegistroSED regAnterior = (RegistroSED)registrosClon[semanaPronostico - 1];
            double alfaAux=1-alfaOptimo;
            //resNideal.pronosticoN1 = alfaOptimo * regAnterior.Demanda + (1 - alfaOptimo) * regAnterior.Pronostico;
            int periodoN1 = regAnterior.Periodo + 1;
            resNideal.pronosticoN1 = ((2 + ((alfaOptimo * periodoN1) / alfaAux)) * regAnterior.St1) - ((1 + ((alfaOptimo * periodoN1) / alfaAux)) * regAnterior.St21);
        }

        public override void calcularModelo(System.Collections.ArrayList registrosF)
        {
            
            this.registros = newListRegistrosSED(registrosF);
            // se evalua que existan mas de 52 datos de pronostico..
            if (registros.Count >= 53)
            {
                //INICIALIZACION
                
                // calculo de la Pendiente
                b2 = calcularPendiente(registrosF);
             
                // calculo de a1=Intercepto
                a1 = calcularIntercepto(b2);

                // calculo de b1
                b1 = a1 + (45 * b2);

                RegistroSED reg45 = new RegistroSED();
                // se asigna el pronostico para la semana 45

                for (double a = 0.001; a < 1; a = a + 0.001)
                {

                    // Calculo de S0inicial
                    S0 = b1 - (((1 - a) / a) * b2);

                    // Calculo de S02inicial
                    S02 = b1 - (2 * (((1 - a) / a) * b2));

                    foreach (RegistroSED reg in registros)
                    {
                        if (reg.Semana == 45)
                        {
                            reg.Periodo = 0;
                            reg.St1 = S0;
                            reg.St21 = S02;
                            reg45 = reg;
                            break;
                        }
                    }

                    ////calculo de la corrida
                    ArrayList pronosticoInicial = pronosticoParcial(reg45, a, registros);
                    ////calculan los resultados ET, ETabsoluto, Etcuadrado
                    Resultado resultadoParcial = new Resultado();
                    resultadoParcial = calcularResultados(pronosticoInicial);
                    resultadoParcial.AlfaOptimo = a;
                    this.resultadosN.Add(resultadoParcial);
                }

                this.resNideal = CalcAlfaOptimo(resultadosN);

                //se asigna el alfa optimo
                this.alfaOptimo = resNideal.AlfaOptimo;
                //se actualizan los S0
                // Calculo de S0inicial
                S0 = b1 - (((1 - alfaOptimo) / alfaOptimo) * b2);

                // Calculo de S02inicial
                S02 = b1 - (2 * (((1 - alfaOptimo) / alfaOptimo) * b2));

                foreach (RegistroSED reg in registros)
                {
                    if (reg.Semana == 45)
                    {
                        reg.Periodo = 0;
                        reg.St1 = S0;
                        reg.St21 = S02;
                        reg45 = reg;
                        break;
                    }
                }
               
                //Pronostico con el alfa ideal
                ArrayList pronosticoFinal = pronosticoParcial(reg45, alfaOptimo, registros);
                registros = pronosticoFinal;
                //calculan los resultados ET, ETabsoluto, Etcuadrado
                resNideal = calcularResultados(pronosticoFinal);
                //se envia el arreglo de registros final
                resNideal.RegistrosFinal = pronosticoFinal;
                //this.mostrarArreglo("SuavExpDoble-Alfa-"+alfaOptimo);

                // se calcula el pronostico para la siguiente semana (N+1) se al macena en el resNIDEal
                pronosticoSemanaN();

            }
            else
            {
                // el archivo no tiene minimo 53 semanas
                System.Windows.Forms.MessageBox.Show("Debe cargar minimo 53 Semanas");
            } 
        }

        //------------------------ FIN METODOS SOBREESCRITOS

        private double calcularIntercepto(double pendiente)
        {
            //valor promedio para los valores de x Semana 1 a la Semana 52 
            double mediaX = 23;
            //valor promedio de la demanada
            double mediaY = promedioDemanda(45);
            return mediaY - (pendiente * mediaX);
        }

        private double calcularPendiente(ArrayList registrosF)
        {
            //valor promedio para los valores de x Semana 1 a la Semana 45 
            double mediaX = 23;
            //valor promedio de la demanada
            double mediaY = promedioDemanda(45);
            //variable auxiliar para realizar la sumatoria del numerador
            double auxNUM = 0;
            //variable auxiliar para realizar la sumatoria del denominador
            double auxDEN = 0;

            foreach (Registro reg in registros)
            {
                if (reg.Semana <= 45)
                {
                    auxNUM += (reg.Semana - mediaX) * (reg.Demanda - mediaY);
                    auxDEN += Math.Pow((reg.Semana - mediaX), 2);
                }
                else
                    break;
            }

            try
            {
                return (auxNUM / auxDEN);
            }
            catch (System.DivideByZeroException ex)
            {
                System.Console.WriteLine("Division by zero attempted!: " + ex.Message);
                return 0;
            }
        }

        // funcion para calcular el promedio de la semana n
        private double promedioDemanda(int semana)
        {
            double sum = 0;
            double prom = 0;
            foreach (Registro reg in registros)
            {
                if (reg.Semana <= semana)
                {
                    sum += reg.Demanda;
                }
                else
                    break;
            }
            prom = sum / semana;
            return prom;
        }


        private System.Collections.ArrayList newListRegistrosSED(System.Collections.ArrayList registros)
        {
            System.Collections.ArrayList registrosClon = new System.Collections.ArrayList();

            foreach (Registro reg in registros)
            {
                RegistroSED regTemp = new RegistroSED(reg);
                registrosClon.Add(regTemp);
            }

            return registrosClon;
        }

        private ArrayList pronosticoParcial(RegistroSED reg45, double alfa, ArrayList registros)
        {
            // se realiza una copia de la lista original, para cada calculo de N
            ArrayList registrosClon = new System.Collections.ArrayList();
            registrosClon = copiarArrayListSED(registros);
            double alfaAux=(1-alfa);
            RegistroSED regAnterior = reg45;
            foreach (RegistroSED reg in registrosClon)
            {
                if (reg.Semana > 45)
                {
                    //se define el periodo
                    reg.Periodo = (regAnterior.Periodo+1);
                    //calculo de St
                    reg.St1=(alfa*reg.Demanda)+(alfaAux*regAnterior.St1);
                    //calculo de St2
                    reg.St21=(alfa*reg.St1)+(alfaAux*regAnterior.St21);
                    // calculo del pronostico
                    reg.Pronostico = ((2+((alfa*reg.Periodo)/alfaAux))*regAnterior.St1)-((1+((alfa*reg.Periodo)/alfaAux))*regAnterior.St21);
                    //se calcula el et
                    reg.Et = reg.Demanda - reg.Pronostico;
                    //se calcula el etabsoluto
                    reg.Etabsoluto = Math.Abs(reg.Et);
                    //se calcula el etcuadrado
                    reg.Etcuadrado = Math.Pow(reg.Et, 2);
                    //se calcula el APE
                    reg.Ape = 100 * (Math.Abs(reg.Et / reg.Demanda));
                    //se calcula el APE1
                    reg.Ape1 = 100 * (Math.Abs(reg.Et / reg.Pronostico));

                    //se actualiza el registro anterior para el proximo pronostico
                    regAnterior = reg;
                }
            }

            return registrosClon;
        }

        private Resultado calcularResultados(ArrayList registros)
        {
            Resultado resultado = new Resultado();

            double sumatoriaMAD = 0;
            double sumatoriaECM = 0;
            double sumatoriaMAPE = 0;
            double sumatoriaMAPE1 = 0;
            double count = 0;

            foreach (RegistroSED reg in registros)
            {
                if (reg.Pronostico != 0)
                {
                    sumatoriaMAD += reg.Etabsoluto;
                    sumatoriaECM += reg.Etcuadrado;
                    sumatoriaMAPE += reg.Ape;
                    sumatoriaMAPE1 += reg.Ape1;
                    count++;
                }
            }

            resultado.ECM = sumatoriaECM / count;
            resultado.MAD = sumatoriaMAD / count;
            resultado.MAPE = sumatoriaMAPE / count;
            resultado.MAPE1 = sumatoriaMAPE1 / count;

            return resultado;
        }

        public void mostrarArreglo(String nombre)
        {
            //Gestion_Inventario_Proveedores.clases.herramientas.FileManager file = new Gestion_Inventario_Proveedores.clases.herramientas.FileManager();
            //file.generarArchivoSED(registros, nombre);
        }

        private double optimizarAlfa(Resultado resultados)
        {
            this.alfaOptimo = 0.01;
            return alfaOptimo;
        }

    }
}
